/*******************************************************************************
 * Copyright (c) 2008 Scott Stanchfield.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Based on the ANTLR parser generator by Terence Parr, http://antlr.org
 *   Ric Klaren <klaren@cs.utwente.nl>
 *   Scott Stanchfield - Modifications for XML Parsing
 *******************************************************************************/
package com.javadude.antxr.scanner;

import java.io.Reader;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import com.javadude.antxr.Parser;
import com.javadude.antxr.Token;
import com.javadude.antxr.TokenStream;
import com.javadude.antxr.TokenStreamException;

/**
 * A simple implementation of XMLPullTokenStream that uses kxml as its
 * implementation
 */
public class BasicMxp1XMLPullTokenStream implements TokenStream {
    private XMLPullTokenStream tokenStream;

    private static final Class<?>[] NO_PARAMETERS = new Class[] {};

    private static final Object[] NO_ARGUMENTS = new Object[] {};

    /**
     * Creates an instance of the KXml token stream
     * @param xmlToParse the xml stream to parse
     * @param parserClass the generated parser class
     * @param namespaceAware do we want a namespace aware parse
     *
     */
    public BasicMxp1XMLPullTokenStream(Reader xmlToParse, Class<? extends Parser> parserClass,
                                        boolean namespaceAware) {
        // Create the XmlPull parser (really part of the scanner)

        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance("org.xmlpull.mxp1.MXParser", null);
            factory.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, namespaceAware);

            XmlPullParser parser = factory.newPullParser();
            parser.setInput(xmlToParse);

            Field field = parserClass.getField("_tokenNames");
            String[] tokenNames = (String[])field.get(null);

            Method getNameSpaceMapMethod = parserClass.getMethod("getNamespaceMap", BasicMxp1XMLPullTokenStream.NO_PARAMETERS);
            @SuppressWarnings("unchecked")
            Map<String, String> namespaceMap = (Map<String, String>)getNameSpaceMapMethod.invoke(null, BasicMxp1XMLPullTokenStream.NO_ARGUMENTS);

            // Create our scanner (using the xml pull parser)
            tokenStream = new XMLPullTokenStream(tokenNames, namespaceMap, parser);
        }
        catch (XmlPullParserException e) {
            throw new RuntimeException("Exception thrown setting up KXml parser. See nested exception.",e);
        }
        catch (NoSuchFieldException e) {
            throw new IllegalArgumentException("Cannot find _tokenNames in the parser class -- is it an XML parser?");
        }
        catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Cannot find getNamespaceMap() in the parser class -- is it an XML parser?");
        }
        catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Cannot access _tokenNames or getNamespaceMap() in the parser class (they should be static)");
        }
        catch (IllegalAccessException e) {
            throw new IllegalArgumentException("Cannot access _tokenNames or getNamespaceMap() in the parser class (they should be public)");
        }
        catch (InvocationTargetException e) {
            throw new RuntimeException("Exception thrown when running getNamespaceMap(). See nested exception.", e);
        }


    }

    /** {@inheritDoc} */
    public Token nextToken() throws TokenStreamException {
        return tokenStream.nextToken();
    }

}
